package com.wangwenjun.concurrency.chapter16;

import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

public class SendMailBusiness {


    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 3 ; i++) {
            new People().start();
        }

        Thread.sleep(2_000);

        for (int i = 0; i < MailBoxes.getKeys().size(); i++) {
            new Postman(i, "内容").start();
        }

    }



}

class People extends Thread{

    @Override
    public void run() {
        GuardedObject guardedObject = MailBoxes.generateGuardedObject();
        Object result = guardedObject.getResult(5000);
        System.out.println(guardedObject.getId()+":"+result);
    }

}

class Postman extends Thread{

    private Integer id;

    private String mail;

    public Postman(Integer id, String mail){
        this.id = id;
        this.mail = id +":"+mail;
    }

    @Override
    public void run() {
        GuardedObject guardedObject = MailBoxes.getGuardedObject(id);
        guardedObject.setResult(mail);
    }
}

class MailBoxes{

    private static final ConcurrentHashMap<Integer, GuardedObject> map = new ConcurrentHashMap<>();

    private static Integer id = 0;

    public static synchronized GuardedObject generateGuardedObject(){
        return map.computeIfAbsent(id++, key -> new GuardedObject(key));
    }

    public static ArrayList<Integer> getKeys(){
        return new ArrayList<>(map.keySet());
    }

    public static GuardedObject getGuardedObject(Integer id){
        return map.remove(id);
    }

}


class GuardedObject{

    private final Object lock = new Object();

    private Object result;

    private Integer id;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public GuardedObject(Integer id){
        this.id = id;
    }

    public Object getResult(long timeout){
        synchronized (lock){
            long beginTime = System.currentTimeMillis();
            long endTime = 0;
            while (result == null){
                try {
                    lock.wait(timeout);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                endTime = System.currentTimeMillis();
                long duration = endTime - beginTime;
                if(duration >= timeout){
                    break;
                }else{
                    timeout = timeout - duration;
                }
            }
            return result;
        }
    }


    public void setResult(Object object){
        synchronized (lock){
            this.result = object;
            lock.notifyAll();
        }
    }
}
